package com.strato.rpc.annotation.handler.v1_0_0;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.osgi.framework.Bundle;

import com.strato.annotation.base.v1_0_0.handler.AbstractAnnotationHandler;
import com.strato.annotation.base.v1_0_0.registry.ClassDescriptor;
import com.strato.annotation.osgi.v1_0_0.service.OsgiService;
import com.strato.annotation.osgi.v1_0_0.service.util.ServiceCreator;
import com.strato.logging.v1_0_0.api.Log;
import com.strato.logging.v1_0_0.api.LogFactory;
import com.strato.rpc.annotation.v1_0_0.RpcService;
import com.strato.rpc.annotation.v1_0_0.RpcWired;
import com.strato.rpc.api.v1_0_0.Registry;

@OsgiService
public class RpcServiceAnnotationHandler extends AbstractAnnotationHandler {

	private Log log=LogFactory.getLog(getClass());

	private static List<Class<? extends Object>> getInterfaces(Class<? extends Object> clazz) {
		List<Class<? extends Object>> interfaces=new ArrayList<Class<? extends Object>>();
		interfaces.addAll(Arrays.asList(clazz.getInterfaces()));
		if(clazz.getSuperclass()!=null&&!clazz.getSuperclass().equals(Object.class)){
			interfaces.addAll(getInterfaces(clazz.getSuperclass()));
		}
		return interfaces;
	}
	
	@Override
	public void handleClass(Bundle bundle, Class<?> clazz, Annotation annotation) {
		if(!RpcService.class.equals(annotation.annotationType())){
			return;
		}
		RpcService a=clazz.getAnnotation(RpcService.class);
		Object service = ServiceCreator.newInstance(bundle, clazz);
		Registry registry = Registry.get(a.registry());
		List<Class<? extends Object>> intfs = getInterfaces(clazz);
		for(Class<? extends Object> intf:intfs){
			registry.register(a.id(),a.transport(),service,intf,bundle.getSymbolicName());
			log.info("Created "+a.transport()+" RPC with id "+a.id()+":"+service+" as "+intf+" in bundle "+bundle.getSymbolicName());
		}
	}

	@Override
	public void handleFieldOnInstantiate(Bundle bundle, Field field, Class<?> clazz, Object object,
			Annotation fieldAnnotation) {
		if (RpcWired.class.equals(fieldAnnotation.annotationType())) {
			{
				Object fieldValue = createFieldValue(bundle, field, clazz, object, fieldAnnotation);
				boolean accessible = field.isAccessible();
				try {
					field.setAccessible(true);
					field.set(object, fieldValue);
					log.info("Wired "+fieldValue.getClass()+" to "+object+"."+field.getName());
				} catch (IllegalArgumentException e) {
					throw new RuntimeException(e);
				} catch (IllegalAccessException e) {
					throw new RuntimeException(e);
				} finally {
					field.setAccessible(accessible);
				}
			}
		}
	}

	private Object createFieldValue(Bundle bundle, Field field, Class<?> clazz, Object object,
			Annotation fieldAnnotation) {
		RpcWired wired=field.getAnnotation(RpcWired.class);
		Registry registry = Registry.get(wired.registry());
		Class<?> fieldType = field.getType();
		Object proxy=registry.createProxy(wired.id(),fieldType,wired.retry());
		return proxy;
	}

	@Override
	public void handleUnresolveClass(Bundle bundle, ClassDescriptor classDescriptor) {
		RpcService a = (RpcService) classDescriptor.getAnnotation(RpcService.class);
		if(a==null){
			return;
		}
		Registry registry = Registry.get(a.registry());
		registry.unregister(a.id());
	}

}
